package webserver;

import java.io.IOException;
import java.net.Socket;

// <editor-fold defaultstate="collapsed" desc=" UML Marker "> 
// #[regen=yes,id=DCE.C536BC0D-A3D7-20F3-97CD-92109CCF572D]
// </editor-fold>
/**
 * Class RequestThread manages an incoming Socket and parses the given informations
 * It will check which type of Request is present and return the correct Request
 * 
 */
public class RequestThread extends Thread {

	/**
	 * Enum representing the state of the current Thread
	 * READREQUEST: Read a complete Request from the socket
	 * PERSISTENT: This socket is part of a persistent connection, wait for new Requests comeing in
	 * CLOSE: No data on socket, can be closed
	 */
	protected enum requestThreadStateEnum {

		READREQUEST, PERSISTENT, CLOSE
	};
	/**
	 * Maximum time (ms) to wait before socket will be closed when no data comes in
	 */
	protected static final int MAX_WAIT_TIME = 100;//10000;
	// <editor-fold defaultstate="collapsed" desc=" UML Marker ">
	// #[regen=yes,id=DCE.0C760493-C24E-E5EF-22D3-DD14540FF283]
	// </editor-fold>
	// Variables
	private Server server;
	private Socket socket;
	private Request request;
	private requestThreadStateEnum requestState;
	/**
	 * The time to wait until the socket can be closed when no data has been received on it
	 */
	protected int wait_time = MAX_WAIT_TIME;

	// <editor-fold defaultstate="collapsed" desc=" UML Marker ">
	// #[regen=yes,id=DCE.C1EB744F-598B-8AFC-FAFC-9AB7C44C4BD3]
	// </editor-fold>
	/**
	 * RequestThread Default Constructor
	 * Starts the Thread in READREQUEST state
	 * @see requestThreadStateEnum
	 * @param server - The server which accepted the Request (parent)
	 * @param socket - The Socket on which the Request was received
	 */
	public RequestThread(Server server, Socket socket) {
		this.requestState = requestThreadStateEnum.READREQUEST;
		this.server = server;
		this.socket = socket;
		this.request = null;

		Logger.writeToLog("Acepting new Connection: " + socket.getInetAddress() + ": " + socket.toString());
		this.start();
	}

	/**
	 * RequestThread Overloaded Constructor
	 * Starts the Thread in PERSISTENT state
	 * @see requestThreadStateEnum
	 * @param server
	 * @param r
	 */
	public RequestThread(Server server, Request r) {
		this.requestState = requestState.PERSISTENT;
		this.server = server;
		this.request = r;
		this.socket = r.getSocket();

		this.start();
	}

	/**
	 * The Run Method for RequestThread
	 * This will take the incoming data from the Socket and check for the request type
	 * Tells the Server the parsed Request
	 */
	@Override
	public void run() {

		boolean wait = false;
		do {
			if (this.requestState == requestThreadStateEnum.READREQUEST) {
				wait = false;

				// Generate new Request
				Request req = Request.parseRequest(this.socket);
				req.setSocket(this.socket);
				req.process();
				this.request = req;
				if (req.getPersistentTimeout() >= 0) {
					this.wait_time = req.getPersistentTimeout(); // Set the timeout if received in Header
				}
				this.server.addNewRequest(this, req);
				break;
			} else {
				// Wait for new Data
				try {
					if (this.socket.getInputStream().available() > 0) {
						this.requestState = requestThreadStateEnum.READREQUEST;
					} else {
						if (wait) {
							this.requestState = requestThreadStateEnum.CLOSE;
							//this.request.setKeepAlive(false);
						} else {
							wait = true;
							synchronized (this.socket.getInputStream()) {
								this.socket.getInputStream().wait(this.wait_time);
							}
						}
					}
				} catch (InterruptedException ie) {
					Logger.writeToLog("run() interrupted: " + ie.toString());
				} catch (IOException io) {
					Logger.writeToLog("Wait for persistent Connection -> IO Error: " + io.toString());
				}
			}
		} while (this.requestState != requestThreadStateEnum.CLOSE);

		this.server.finishedRequest(this, this.request);
	}

	/**
	 * Tell the Thread to finish immediately
	 */
	public void finish() {
		System.out.println("Socket closed now");
		try {
			this.interrupt();

			this.request.finish();

		} catch (SecurityException se) {
			// ignore :)
		}
	}
}

